#include <LitStdAfx.h>
#include <LitXmlSceneManager.h>
#include <LitExceptions.h>

template<> Lit::XmlSceneManager *Ogre::Singleton<Lit::XmlSceneManager>::msSingleton = 0;

namespace Lit
{
	XmlScene::XmlScene( const XmlScene &rk ) : mSceneMgr( rk.mSceneMgr ), mMap(rk.mMap), mNode(rk.mNode){}
	XmlScene::XmlScene(Ogre::ResourceManager* creator, const Ogre::String &name, 
						Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, 
						Ogre::ManualResourceLoader *loader)
		: Ogre::Resource(creator, name, handle, group, isManual, loader)
		, mSceneMgr( NULL )
		, mNode(NULL)
	{
		/* If you were storing a pointer to an object, then you would set that pointer to NULL here.
		*/
 
		/* For consistency with StringInterface, but we don't add any parameters here
		That's because the Resource implementation of StringInterface is to
		list all the options that need to be set before loading, of which 
		we have none as such. Full details can be set through scripts.
		*/ 
		createParamDictionary("Scene");
	}
 
	XmlScene::~XmlScene()
	{
		unload();
	}
 
	// farm out to SceneSerializer
	void XmlScene::loadImpl()
	{
		XmlSceneSerializer serializer;
		Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(mName, mGroup, true, this);
		serializer.importScene(stream, this);
	}
 
	void XmlScene::unloadImpl()
	{
		/* If you were storing a pointer to an object, then you would check the pointer here,
		and if it is not NULL, you would destruct the object and set its pointer to NULL again.
		*/
 
		clear();
	}
 
	size_t XmlScene::calculateSize() const
	{
		return mMap.size();
	}
 
	void XmlScene::loadFromStream( Ogre::DataStreamPtr& pStream )
	{
		Ogre::DotSceneLoader ldr;
		ldr.parseDotSceneStream(pStream, mGroup, *mSceneMgr, *mNode);
	}
	void XmlScene::loadFromFile( const String &fname ){
		Ogre::DotSceneLoader ldr;
		ldr.parseDotScene(fname, mGroup, *mSceneMgr, *mNode);
	}
		
	void XmlScene::clear()
	{
		destroyScene();
	}

		
	void XmlScene::createScene( const String &filename, Ogre::SceneManager *mgr, const String& resource_group )
	{
		mSceneMgr = mgr;
		assert( mSceneMgr );
		if( mNode ) throw RuntimeError( EXC_ITEM_DUPLICATE, "Cannot create scene, already created, destroy it first", EXC_LOC );
		mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		if( !mLoader.parseDotScene( filename.str(), resource_group, *mSceneMgr, *mNode ) )
			throw RuntimeError( EXC_RUNTIME_ERROR, "Scene '"+filename.str()+"' could not be loaded because\r\n"+mLoader.lastError(), EXC_LOC );
	}
	void XmlScene::createScene( Ogre::DataStreamPtr &strm, Ogre::SceneManager *mgr, const String& resource_group )
	{
		mSceneMgr = mgr;
		assert( mSceneMgr );
		if( mNode ) throw RuntimeError( EXC_ITEM_DUPLICATE, "Cannot create scene from stream , already created, destroy it first", EXC_LOC );
		if( !mLoader.parseDotSceneStream( strm, resource_group, *mSceneMgr, *mNode ) )
			throw RuntimeError( EXC_RUNTIME_ERROR, "Scene could not be loaded from stream because\r\n"+mLoader.lastError(), EXC_LOC );
	}
	void XmlScene::_destroyChildNodes( Ogre::SceneNode* n )
	{
		auto i = mNode->getChildIterator();
		while(i.hasMoreElements())
		{
			_destroyChildNodes( (Ogre::SceneNode*)i.getNext() );
		}
		auto j=n->getAttachedObjectIterator();
		while( j.hasMoreElements() )
		{
			Ogre::MovableObject * e= j.getNext();
			Ogre::SceneNode*node = e->getParentSceneNode();
			Ogre::LogManager::getSingleton().logMessage("Destroying '"+e->getName()+"'");
			node->detachAllObjects();
			mSceneMgr->destroySceneNode( node );
		}
		n->detachAllObjects();
		mSceneMgr->destroySceneNode( n );
	}
	void XmlScene::destroyScene( )
	{
		if( !mNode ) return;
		_destroyChildNodes( *mNode );
		mNode = NULL;
	}
	void XmlScene::setSceneManager( OgreSceneManagerPtr mgr ){ mSceneMgr = mgr; }
	OgreSceneManagerPtr XmlScene::sceneManager() const{ return mSceneMgr; }
 
	XmlSceneSerializer::XmlSceneSerializer()
	{
	}
 
	XmlSceneSerializer::~XmlSceneSerializer()
	{
	}
 
	void XmlSceneSerializer::exportScene(const XmlScene *pText, const Ogre::String &fileName)
	{
		/*std::ofstream outFile;
		outFile.open(fileName.c_str(), std::ios::out);
		outFile << pText->getString();
		outFile.close();*/
	}
 
	void XmlSceneSerializer::importScene(Ogre::DataStreamPtr &stream, XmlScene *pDest)
	{
		pDest->loadFromStream( stream );
	}
 
	XmlSceneManager *XmlSceneManager::getSingletonPtr()
	{
		return msSingleton;
	}
 
	XmlSceneManager &XmlSceneManager::getSingleton()
	{  
		assert(msSingleton);  
		return(*msSingleton);
	}
	std::string XmlSceneManager::RESOURCE_TYPE = std::string("Scene");

	XmlSceneManager::XmlSceneManager(Ogre::SceneManager * mgr): mSceneMgr(mgr)
	{
		mResourceType = RESOURCE_TYPE;
 
		// low, because it will likely reference other resources
		mLoadOrder = 30.0f;
 
		// this is how we register the ResourceManager with OGRE
		Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
		Ogre::ResourceGroupManager::getSingleton()._registerScriptLoader(this);
	}
 
	XmlSceneManager::~XmlSceneManager()
	{
		// and this is how we unregister it
		Ogre::ResourceGroupManager::getSingleton()._unregisterScriptLoader(this);
		Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
	}
	
	void XmlSceneManager::initialise(OgreRenderWindowPtr win, OgreCameraPtr cam, OgreSceneManagerPtr mgr )
	{ 
		mSceneMgr = *mgr;
	}
	void XmlSceneManager::shutdown()
	{
	}
	XmlScenePtr XmlSceneManager::load(const Ogre::String &name, const Ogre::String &group)
	{
		XmlScenePtr textf = getByName(name);
 
		if (textf.isNull())
			textf = create(name, group);
		textf->setSceneManager( mSceneMgr );
		textf->load();
		return textf;
	}
	Ogre::Resource *XmlSceneManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, 
												const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, 
												const Ogre::NameValuePairList *createParams)
	{
		return new XmlScene (this, name, handle, group, isManual, loader);
	}	
	const Ogre::StringVector& XmlSceneManager::getScriptPatterns(void){
		static Ogre::StringVector ret;
		if( ret.empty() )
		{
			ret.push_back("*.scene");
		}
		return ret;
	}
	/** Parse a script file.
	@param stream Weak reference to a data stream which is the source of the script
	@param groupName The name of a resource group which should be used if any resources
		are created during the parse of this script.
	*/
	void XmlSceneManager::parseScript(Ogre::DataStreamPtr& stream, const Ogre::String& groupName)
	{
		XmlScenePtr textf = getByName(stream->getName());
 
		if (textf.isNull())
			textf = create(stream->getName(), groupName);
		stream->close();
		textf->setSceneManager( mSceneMgr );
		textf->load();
	}

	/** Gets the relative loading order of scripts of this type.
	@remarks
		There are dependencies between some kinds of scripts, and to enforce
		this all implementors of this interface must define a loading order. 
	@returns A value representing the relative loading order of these scripts
		compared to other script users, where higher values load later.
	*/
	Ogre::Real XmlSceneManager::getLoadingOrder(void) const{ return 500.0f; }
};